package com.huc.tools.tools.rsa;

import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.http.fileupload.IOUtils;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author: 胡成
 * @Version: 0.0.1V
 * @Date: 2018/8/9
 * @Description: RSA加密算法
 **/
public class RSATools {
    public static final String CHARSET = "utf-8";
    public static final String RSA_ALGORITHM = "RSA";

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 9:18
     * @Description: 生成公私钥
     **/
    public static Map<String, String> createKeys(int keySize) {
        //为RSA算法创建一个keyPairGenerator
        KeyPairGenerator kpg = null;
        try {
            kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        kpg.initialize(keySize);
        //生成密钥对
        KeyPair keyPair = kpg.generateKeyPair();
        Key publicKey = keyPair.getPublic();
        String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
        //得到私钥
        Key privateKey = keyPair.getPrivate();
        String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
        Map<String, String> keyPairMap = new LinkedHashMap<>();
        keyPairMap.put("publicKey", publicKeyStr);
        keyPairMap.put("privateKey", privateKeyStr);
        return keyPairMap;
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 9:32
     * @Description: 得到公钥
     **/
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509EncodedKeySpec);
        return key;
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 9:36
     * @Description: 得到私钥
     **/
    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        return key;
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 9:53
     * @Description: 私钥解密
     **/
    public static String decrypt(String data, String key, Integer flag) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            if (flag == 1) {
                RSAPublicKey publicKey = getPublicKey(key);
                cipher.init(Cipher.DECRYPT_MODE, publicKey);
                return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);
            }
            RSAPrivateKey privateKey = getPrivateKey(key);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * @Author: 胡成
     * @Date: 2018/8/9 9:54
     * @Description: 公私钥加密
     **/
    public static String encrypt(String data, String key, Integer flag) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            if (flag == 1) {
                RSAPublicKey publicKey = getPublicKey(key);
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
            }
            RSAPrivateKey privateKey = getPrivateKey(key);
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
        int maxBlock = 0;
        if (opmode == Cipher.DECRYPT_MODE) {
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try {
            while (datas.length > offSet) {
                if (datas.length - offSet > maxBlock) {
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(datas, offSet, datas.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (Exception e) {
            throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();
        IOUtils.closeQuietly(out);
        return resultDatas;
    }

    public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException {
        String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK9YrXAQEAukjYvui0TdPc1ffKJSv3RKtBkn0ul_V7w2c59FxoliCJC6INvEDaC96VjkCn4TxkFl-0jrCXwAU7kCAwEAAQ";
        String privateKey = "MIIBVwIBADANBgkqhkiG9w0BAQEFAASCAUEwggE9AgEAAkEAr1itcBAQC6SNi-6LRN09zV98olK_dEq0GSfS6X9XvDZzn0XGiWIIkLog28QNoL3pWOQKfhPGQWX7SOsJfABTuQIDAQABAkEAlonydRhFPkmrACzvQlMTQSOudAnItwV_s20eRTr_zuQgxwJ9V-1ijdxDzZ4r7IEZrWt2ie3altF77e7ic5wggQIhANsGFmW94dwYIxe-yS9wtbYPuh4Bb0-Y1Xo5nymsxQ9pAiEAzPLpkmxeO9y76ylpDzbZAOAHVMJ49HzETk3CGyyf59ECIQDUXB-lQClYlFKaOFKq27ZjsvX7F9sMKxx5KJto9eoPWQIhAInrgmGmCbhRMCjt4iR57YjYOjbA9HLbL6PeUhzFaAqxAiEAz_uGhRI5NyJC3CTpumtC9BOwYMiQTPEVYaghvqaCFlQ";
//        System.out.println(createKeys(512));
//        System.out.println(getPublicKey(publicKey));
        System.out.println(encrypt("123", privateKey, 2));
        System.out.println(decrypt("Rk5eK-BGW6nftr1q8vIqBo1d0kX8Qike_Nbtis3DBMLYdvGmXjtVX6SUwqvnUQvT7eYdMypFgGf4YOlDyIgaRw", publicKey, 1));

    }
}
